12.5.6 バックプレッシャー
仕事量を制限し、システム全体として効率よく動作させるテクニックとしてバックプレッシャーがある
バッファ付きチャネルとselect文を使って同時リクエストの数を制限する
code:go
package main
import (
"errors"
"fmt"
"net/http"
"time"
)
// PressureGauge は同時実行できる処理数を制御する構造体
// チャネルを用いて、過負荷時にはリクエストを拒否する「バックプレッシャー」を実現する
type PressureGauge struct {
ch chan struct{}
// 空の構造体はメモリを消費しない
// 入出だけがわかる「シグナル」として用いる
}
func New(limit int) *PressureGauge {
return &PressureGauge{
ch: make(chan struct{}, limit),
}
}
// *PressureGauge pgを受け取って、インスタンスが許す範囲で関数fを実行する
func (pg *PressureGauge) Process(f func()) error {
// select文を使うことで、チャネルが満杯ならブロックせずに即座にエラーを返す
select {
case pg.ch <- struct{}{}: // チャネルpg.chに書き込み
f() // Processに引数にとして渡された関数を実行
<-pg.ch // チャネルのバッファを一つ解放する
return nil
default:
return errors.New("キャパシティに余裕がありません")
}
}
func doThingThatShouldBeLimited() string {
time.Sleep(2 * time.Second) // 2秒スリープ
return "完了\n"
}
func main() {
pg := New(5)
http.HandleFunc("/request", func(w http.ResponseWriter, r *http.Request) {
err := pg.Process(func() {
w.Write([]byte(doThingThatShouldBeLimited()))
})
if err != nil {
w.WriteHeader(http.StatusTooManyRequests)
w.Write([]byte("リクエストが多すぎてさばききれません\n"))
}
})
http.ListenAndServe(":8080", nil)
}